package com.bidevalution.business.vote.service.impl;

import com.bidevalution.business.enums.VoteStatusEnum;
import com.bidevalution.business.exception.VoteCallbackException;
import com.bidevalution.business.exception.VoteException;
import com.bidevalution.business.service.BeSectionStepJobService;
import com.bidevalution.business.vote.callbackService.VoteBaseCallback;
import com.bidevalution.business.vote.service.BeExpertVoteService;
import com.bidevalution.business.vote.vi.CallStepJobVi;
import com.bidevalution.dao.entity.*;
import com.bidevalution.dao.mapper.BeExpertVoteDetailMapper;
import com.bidevalution.dao.mapper.BeExpertVoteMapper;
import com.bidevalution.dao.mapper.BeSectionExpertInfoMapper;
import com.bidevalution.support.context.Constants;
import com.bstek.ureport.Utils;
import com.qtp.common.data.RedisAccess;
import com.qtp.core.redis.RedisUtil;
import com.qtp.core.util.GsonUtil;
import com.qtp.core.util.OConvertUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * Created by 查慧英 on 2020-04-08.
 */
@Service(value = "beExpertVoteService")
public class BeExpertVoteServiceImpl implements BeExpertVoteService {
    private static final String SECTION_ID = "sectionId";
    private static final String EXPERT_RESULE = "expertResule";
    private static final String VOTE_SUB_ID = "voteSubId";
    private static final String NEXT_JOB = "nextJob";
    private static final String REMARk = "remark";
    private static final String EXPERT_ID = "expertId";
    private static final String VOTE_STATUS = "voteStatus";
    private static final String LOGIN_NAME = "loginName";
    private static final String VOTE_ALGORITHM = "voteAlgorithm";
    private static final String SEQ_ID = "seqId";
    private static final String VOTE_RESULE = "voteResule";
    private static final String SECTION_STEP_ID = "sectionStepId";
    private static final String VOTE_PARAMS = "voteParams";

    private static final int REDIS_EXPIRE_TIME = 100;//14400;
    private static final String FORMAT_LIST = "voteList_%s_%s";
    private static final String FORMAT_REDIS = "voteList_%s";
    private static final String REDIS_VOTEING = "voteing_%s";
    @Autowired(required = false)
    BeExpertVoteMapper beExpertVoteMapper;
    @Autowired(required = false)
    BeExpertVoteDetailMapper beExpertVoteDetailMapper;
    @Autowired(required = false)
    private BeSectionExpertInfoMapper beSectionExpertInfoMapper;
    @Autowired
    private BeSectionStepJobService beSectionStepJobService;

    @Override
    public void createVote(List<BeExpertVoteExt> expertVoteList) throws VoteException {
        if(expertVoteList == null || expertVoteList.isEmpty()) {
            throw new VoteException("表决列表为空!");
        }

        BeExpertVoteExt beExpertVoteExt = expertVoteList.get(0);
        if (beExpertVoteExt.getVoteDetailList().isEmpty()) {
            throw new VoteException("参与表决专家不能为空！");
        }

        String sectionId = beExpertVoteExt.getSectionId();
        String voteingKey = String.format(REDIS_VOTEING, sectionId);
        String voting = RedisUtil.get(voteingKey);
        if (!StringUtils.isEmpty(voting)) {
            throw new VoteException("正在表决中，不能新建！");
        }

        RedisUtil.set(voteingKey, sectionId);
        RedisUtil.expire(voteingKey, REDIS_EXPIRE_TIME);
        ((BeExpertVoteServiceImpl)AopContext.currentProxy())._createVoteByTransactional(expertVoteList);
        deleteRedis(sectionId);
    }

    @Transactional(rollbackFor = Exception.class)
    public void _createVoteByTransactional(List<BeExpertVoteExt> expertVoteList) {
        for (BeExpertVoteExt expertVoteExt: expertVoteList) {
            BeExpertVote expertVote = expertVoteExt.getNewBeExpertVote();
            String voteId = UUID.randomUUID().toString();
            expertVote.setSeqId(voteId);
            expertVote.setVoteStatus(VoteStatusEnum.Start.code);
            beExpertVoteMapper.insertSelective(expertVote);
            List<BeExpertVoteDetailExt> voteDetailList = expertVoteExt.getVoteDetailList();
            if (voteDetailList != null && !voteDetailList.isEmpty()) {
                addExpertVoteDetail(voteId, voteDetailList);
            }
        }
    }

    @Override
    public void submitVote(String voteJson, String loginName) throws VoteException {
        if(StringUtils.isEmpty(voteJson)) {
            throw new VoteException("表决信息不能为空");
        }
        List<Map<String, Object>> list = GsonUtil.jsonToListMapWithGson(voteJson);
        if(list == null || list.isEmpty()) {
            throw new VoteException("提交表决为空！");
        }

        Map<String, Object> map = list.get(0);
        String sectionId = OConvertUtils.getString(map.get(SECTION_ID));
        ((BeExpertVoteServiceImpl)AopContext.currentProxy())._submitVoteByTransactional(list);
        //删除redis中的列表,更新redis
        deleteRedis(sectionId);
    }

//    @Transactional(rollbackFor = Exception.class)
    public void _submitVoteByTransactional(List<Map<String, Object>> list) throws VoteException {
        Map<String, Object> map = list.get(0);
        String sectionId = OConvertUtils.getString(map.get(SECTION_ID));
        BeExpertVoteDetail voteDetail;
        for (Map<String, Object> item : list) {
            voteDetail = new BeExpertVoteDetail();
            voteDetail.setSeqId(OConvertUtils.getString(item.get(VOTE_SUB_ID)));
            voteDetail.setVoteResule(OConvertUtils.getString(item.get(EXPERT_RESULE)));
            voteDetail.setRemark(OConvertUtils.getString(item.get(REMARk)));
            voteDetail.setUpdateTime(new Date());
            beExpertVoteDetailMapper.updateByPrimaryKeySelective(voteDetail);
        }
        //最后一个提交的专家统计最终的表决结果并更新表决完成状态及最终表决结果、调回调类
        judgeLastSubmit(sectionId, map);
    }

    @Override
    public List<Map> findVoteListByDoing(Map<String, Object> params) throws VoteException {
        String expertId = getExpertId(params);
        params.put(EXPERT_ID, expertId);
        String sectionId = OConvertUtils.getString(params.get(SECTION_ID));
        String listKey = String.format(FORMAT_LIST, sectionId, expertId);
        String redisKey = String.format(FORMAT_REDIS, sectionId);
        Map<String, Object> redisMap = RedisAccess.getValue(Map.class, redisKey);
        List<Map> list = null;
        if (redisMap != null && !redisMap.isEmpty()) {
            list = (List<Map>) redisMap.get(listKey);
        }
        if (list == null) {
            list = updateRedisList(params);
        }
        return list;
    }

    @Override
    public List<Map> findVotingListByRecord(Map<String, Object> params)throws VoteException{
        String expertId = getExpertId(params);
        params.put(EXPERT_ID, expertId);
        return beExpertVoteMapper.getList(params);
    }

    @Override
    public void addVoteMsg(String sectionId, String msg) throws VoteException {
        String loginName,key;
        List<BeSectionExpertInfo> expertList = beSectionExpertInfoMapper.getList(sectionId, Constants.EXPERT_NO_AVOID);
        for (BeSectionExpertInfo expertInfo : expertList) {
            loginName = expertInfo.getLoginName();
            key = String.format(Constants.VOTE_MSG_TEMPLATE, sectionId, loginName);
            RedisUtil.set(key,msg);
            RedisUtil.expire(key,24*60*60);
        }
    }

    /**
     *  新增表决子表数据
     * @param voteId 投票主表Id
     * @param voteDetailList 表决子表数据列表
     */
    private void addExpertVoteDetail(String voteId, List<BeExpertVoteDetailExt> voteDetailList) {
        BeExpertVoteDetail voteDetail;
        for (BeExpertVoteDetailExt item : voteDetailList) {
            voteDetail = new BeExpertVoteDetail();
            voteDetail.setSeqId(UUID.randomUUID().toString());
            voteDetail.setVoteId(voteId);
            voteDetail.setExpertId(item.getExpertId());
            voteDetail.setOwnJudge(item.getOwnJudge());
            voteDetail.setCreateTime(new Date());
            beExpertVoteDetailMapper.insertSelective(voteDetail);
        }
    }

    /**
     * 判断是否是最后一个专家提交
     * @param sectionId 标段Id
     * @param map 表决列表第一条数据对象
     */
    private void judgeLastSubmit(String sectionId, Map<String, Object> map) throws VoteException {
        long count = beExpertVoteMapper.judgeLastSubmit(sectionId);
        if (count == 0) {
            Boolean delRedisFlag = true;
            String nextJob = OConvertUtils.getString(map.get(NEXT_JOB));
            int voteAlgorithm = OConvertUtils.getInt(map.get(VOTE_ALGORITHM));
            //更新表决完成状态及最终表决结果
            List<BeExpertVoteCallBack> voteCallBackList = null;
            if (voteAlgorithm == 0) {
                //获取少数服从多数表决情况列表
                List<Map<String, Object>> list = beExpertVoteMapper.getMajorityRulesVoteResultList(sectionId);
                voteCallBackList = votePassedResultList(sectionId, nextJob, map, list);
            } else {
                //获取一票否决算法，全票通过的列表
                List<Map<String, Object>> list = beExpertVoteMapper.getOneVoteNoVoteResultList(sectionId);
                voteCallBackList = votePassedResultList(sectionId, nextJob, map, list);
                //一票否决未通过的处理,再次发起表决
                delRedisFlag = voteNotPassedCallForVote(sectionId);
            }
            //回调类更新评审详情及汇总结果表中的数据
            if (voteCallBackList != null && !voteCallBackList.isEmpty()) {
                VoteBaseCallback voteService = getEO(nextJob);
                if (voteService != null) {
                    CallStepJobVi callStepJobVi = voteService.callback(voteCallBackList);
                    if (callStepJobVi != null) {
                        try {
                            List<String> experts = new ArrayList<>();
                            List<BeSectionExpertInfo> beSectionExpertInfos = beSectionExpertInfoMapper.getListBySectionId(sectionId);
                            for (BeSectionExpertInfo item: beSectionExpertInfos) {
                                experts.add(item.getSeqId());
                            }
                            beSectionStepJobService.resetUnFinishJobState( experts, callStepJobVi.getBeSectionStepId());
                            beSectionStepJobService.callStepJobBySumbit(callStepJobVi);
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new VoteCallbackException(e);
                        }
                    }
                }
            }
            if (delRedisFlag) {
                RedisUtil.delete(sectionId);
            }
        }
    }

    /**
     *  一票否决的重新发起表决处理
     * @param sectionId 标段id
     */
    private Boolean voteNotPassedCallForVote(String sectionId) {
        List<BeExpertVote> voteList = beExpertVoteMapper.selectBySectionId(sectionId);
        if (voteList != null && !voteList.isEmpty()) {
            BeExpertVote item = new BeExpertVote();
            item.setUpdateTime(new Date());
            item.setSectionId(sectionId);
            item.setVoteStatus(VoteStatusEnum.End.code);
            beExpertVoteMapper.updateStatusBySectionId(item);
            String seqId;
            for (BeExpertVote vote : voteList) {
                seqId = vote.getSeqId();
                vote.setSeqId(UUID.randomUUID().toString());
                vote.setCreateTime(new Date());
                vote.setUpdateTime(new Date());
                beExpertVoteMapper.insertSelective(vote);
                List<BeExpertVoteDetail> voteDetailList = beExpertVoteDetailMapper.getVoteListByVoteId(seqId);
                if (voteDetailList != null && !voteDetailList.isEmpty()) {
                    for (BeExpertVoteDetail voteDetail : voteDetailList) {
                        voteDetail.setSeqId(UUID.randomUUID().toString());
                        voteDetail.setRemark(null);
                        voteDetail.setVoteResule(null);
                        voteDetail.setCreateTime(new Date());
                        voteDetail.setUpdateTime(new Date());
                        voteDetail.setVoteId(vote.getSeqId());
                        beExpertVoteDetailMapper.insertSelective(voteDetail);
                    }
                }
            }
            return false;
        }
        return true;
    }

    /**
     * 组装表决完后回调需要的数据
     * @param sectionId  标段Id
     * @param nextJob 回调类
     * @param map 表决列表第一条数据对象
     * @param list 表决情况列表
     * @return 回调需要的数据列表
     */
    private List<BeExpertVoteCallBack> votePassedResultList(String sectionId, String nextJob, Map<String, Object> map, List<Map<String, Object>> list) {
        BeExpertVote expertVote;
        List<BeExpertVoteCallBack> voteCallBackList = new ArrayList<>();
        BeExpertVoteCallBack voteCallBack;
        String voteResule;
        String seqId;
        for (Map<String, Object> item : list) {
            voteResule = OConvertUtils.getString(item.get(VOTE_RESULE));
            seqId = OConvertUtils.getString(item.get(SEQ_ID));
            expertVote = new BeExpertVote();
            expertVote.setSeqId(seqId);
            expertVote.setVoteStatus(VoteStatusEnum.End.code);
            expertVote.setVoteResule(voteResule);
            expertVote.setUpdateTime(new Date());
            beExpertVoteMapper.updateByPrimaryKey(expertVote);
            if (OConvertUtils.isNotEmpty(nextJob)) {
                //组装回调的参数
                voteCallBack = new BeExpertVoteCallBack();
                String sectionStepId = OConvertUtils.getString(map.get(SECTION_STEP_ID));
                String voteParams = OConvertUtils.getString(item.get(VOTE_PARAMS));
                voteCallBack.setSectionId(sectionId);
                voteCallBack.setSectionStepId(sectionStepId);
                voteCallBack.setVoteParams(voteParams);
                voteCallBack.setSeqId(seqId);
                voteCallBack.setVoteResule(voteResule);
                //每个专家表决情况列表
                List<BeExpertVoteDetailExt> voteDetailList = beExpertVoteDetailMapper.getExpertVoteListByVoteId(seqId);
                voteCallBack.setVoteDetailList(voteDetailList);
                voteCallBackList.add(voteCallBack);
            }
        }
        return voteCallBackList;
    }

    /**
     * 根据code获取接口实现
     * @param cls 回调类
     * @return 结果
     */
    private VoteBaseCallback getEO(String cls) {
        String tmpCls = StringUtils.isBlank(cls) ? "" : cls;
        //用code获取cls
        if(StringUtils.isNotBlank(tmpCls)) {
            return (VoteBaseCallback) Utils.getApplicationContext().getBean(tmpCls);
        }
        return null;
    }

    /**
     * 更新redis中的表决列表
     * @param params 参数
     * @return 表决列表
     */
    private List<Map> updateRedisList(Map<String, Object> params) {
        String sectionId = OConvertUtils.getString(params.get(SECTION_ID));
        String expertId = OConvertUtils.getString(params.get(EXPERT_ID));
        List<Map> list = beExpertVoteMapper.getList(params);
        String listKey = String.format(FORMAT_LIST, sectionId, expertId);
        String redisKey = String.format(FORMAT_REDIS, sectionId);
        Map<String, Object> redisMap = RedisAccess.getValue(Map.class, redisKey);
        if (redisMap == null || redisMap.isEmpty()) {
            redisMap = new HashMap<>(16);
        }
        redisMap.remove(listKey);
        if (list == null || list.isEmpty()) {
            deleteRedis(sectionId);
        } else {
            redisMap.put(listKey, list);
            RedisAccess.add(redisMap, redisKey);
        }
        RedisUtil.expire(redisKey, REDIS_EXPIRE_TIME);
        return list;
    }
    /**
     * 获取专家信息
     * @param parameters 参数
     * @return 专家信息
     */
    private String getExpertId(Map<String,Object> parameters) {
        String expertId = null;
        String loginName = OConvertUtils.getString(parameters.get(LOGIN_NAME));
        String sectionId = OConvertUtils.getString(parameters.get(SECTION_ID));
        BeSectionExpertInfo expertInfo = beSectionExpertInfoMapper.selectByLoginName(sectionId, loginName);
        if (expertInfo != null) {
            expertId = expertInfo.getSeqId();
        }
        return expertId;
    }
    /**
     * 删除redis
     * @param sectionId 标段Id
     */
    private void deleteRedis(String sectionId) {
        String redisKey = String.format(FORMAT_REDIS, sectionId);
        RedisUtil.delete(redisKey);
    }

}
